Skip to content

fix(terminal): use detected shell for execa and vscode terminal creation (#321)#333

Open
F915 wants to merge 8 commits into
Zoo-Code-Org:mainfrom
F915:main
Open

fix(terminal): use detected shell for execa and vscode terminal creation (#321)#333
F915 wants to merge 8 commits into
Zoo-Code-Org:mainfrom
F915:main

Conversation

@F915
Copy link
Copy Markdown

@F915 F915 commented May 26, 2026

Related GitHub Issue

Closes: #321

Description

Zoo Code correctly detects the user's configured default shell (e.g., PowerShell) and tells the AI to generate commands using that shell's syntax, but the Inline Terminal actually executed commands in cmd.exe. This caused:

  • PowerShell-specific commands (like Write-Host) to fail with "not recognized" errors
  • Non-ASCII output garbling on systems with non-ASCII locales — cmd.exe encodes output using the system codepage (e.g., CP936 for zh-CN) rather than UTF-8

This PR connects shell detection to both terminal execution paths so the user's configured shell is used consistently.


Phase 1 — Connect shell detection to execution paths

Core changes (2 files):

  1. ExecaTerminalProcess.ts — Changed shell: true to shell: getShell(). The detected shell from VS Code's terminal.integrated.defaultProfile is now passed to execa subprocess execution.

  2. Terminal.ts — Added shellPath: getShell() to vscode.window.createTerminal(). Also added iconPath: new vscode.ThemeIcon("rocket").

Test updates (2 files):

  1. ExecaTerminalProcess.spec.ts — Assertions updated from shell: true to shell: "/mock/fallback-shell" using vitest.spyOn pattern.

  2. TerminalRegistry.spec.ts — Assertions updated to include the new shellPath parameter.


Phase 2 — Consolidate shell detection and fix WSL support

After review feedback from @edelauna, we identified that passing getShell() as shellPath on Windows with WSL configured was problematic — getShell() could return a Unix path (/bin/bash) that isn't valid as a Windows shellPath. Further analysis revealed deeper issues: the existing shell detection approach (manual VS Code config parsing + allowlist) could diverge from VS Code's internal shell resolution, creating a mismatch between the external terminal's shell and the fallback shell.

5. shell.ts — Removed SHELL_ALLOWLIST (~95 entries) and manual config parsing functions (getWindowsShellFromVSCode, getMacShellFromVSCode, getLinuxShellFromVSCode). getShell() now reads vscode.env.shell directly (the API VS Code has provided since 1.37), falling back through os.userInfo(), COMSPEC/SHELL environment variables, and platform defaults. Removed getShellFallbackOccurred(). Added WSL_EXE_PATH export for canonical WSL path comparison.

6. Terminal.ts — Three-branch constructor replaces the single shellPath: getShell() call:

  • WSL detected (getWslProfile()): omit shellPath entirely — lets VS Code's WSL profile handle shell selection and PTY bridge setup.
  • execaShellPath set: pass explicit shellPath as the user configured.
  • Default: omit shellPath — defers to VS Code's profile system.

The shellIntegrationReady promise is now started in the constructor (event-based detection + polling + timeout), reducing per-command wait time.

7. TerminalProcess.ts — Three corrections to the fallback path:

  • Removed the getShellFallbackOccurred() check. This check guarded against a mismatch between the terminal's shell and the detected shell, but after the shell.ts refactoring both paths use the same VS Code resolution — the mismatch scenario no longer exists.
  • Removed a sendText call that previously fired before the no_shell_integration fallback. When shell integration was unavailable, the code would call terminal.sendText(command) (executing the command in the external terminal without output capture), then emit no_shell_integration (triggering execa re-execution) — resulting in unintended double execution. The fallback now emits no_shell_integration directly.
  • Missing OSC 633/133 markers (which occurs on WSL where the PTY bridge can reorder or omit these sequences) are now treated as complete output instead of an error, preventing spurious failures.

8. ExecaTerminalProcess.ts — Uses BaseTerminal.getExecaShellPath() || getShell() for unified shell resolution, matching Terminal.ts. When the resolved shell is wsl.exe, spawns it directly with array arguments (--cd <wsl-path> -- bash -c <command>) instead of wrapping through a shell, avoiding nested-quoting issues. Path conversion handles Windows drive letters → /mnt/<drive>/ mapping.

9. Tests — Updated shell.spec.ts for the new getShell() fallback chain. Updated TerminalProcess.spec.ts, TerminalProcess.test.ts, TerminalRegistry.spec.ts, ExecaTerminalProcess.spec.ts, and platform-specific TerminalProcessExec test files to match new behavior. Extended vscode.js mock with onDidChangeTerminalShellIntegration event and env.shell. Fixed backslash escape issues in Windows test paths (\W, \S, \c, \P, \7 are interpreted as escape sequences in JavaScript string literals). Added WSL direct-spawn test in ExecaTerminalProcess.spec.ts.

Known limitation

WSL terminals do not support VS Code shell integration — the supported shells on Windows are limited to Git Bash and pwsh. The OSC 633/133 sequences that shell integration relies on don't traverse the WSL PTY bridge reliably. In practice, the external terminal will wait for shell integration until timeout, then fall back to the inline terminal (execa) — this is expected behavior. The inline terminal path correctly executes WSL commands via direct wsl.exe spawn.

Test Procedure

Unit tests:

cd src
pnpm test -- --run utils/__tests__/shell.spec.ts
pnpm test -- --run integrations/terminal/__tests__/

Type checking:

pnpm check-types

Manual verification (Windows with PowerShell configured):

Prerequisites: PowerShell configured with UTF-8 encoding.

  • Run Write-Host "test" in Zoo Code Inline Terminal
    • Before: 'Write-Host' is not recognized as an internal or external command
    • After: test
  • Run echo "中文测试" in Zoo Code Inline Terminal
    • Before: garbled output (cmd.exe uses system codepage, e.g., CP936 for zh-CN)
    • After: 中文测试 (PowerShell with UTF-8 configured)

WSL verification (Windows with WSL configured as default profile):

  • Inline terminal should execute commands via wsl.exe without nested-quoting errors.
  • External terminal falls back to inline terminal after shell integration timeout.

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Screenshots / Video

N/A — backend fix with no visible UI changes.

Documentation Updates

  • Yes, documentation updates are required.

The shell integration docs need the following updates:

  1. "Use Inline Terminal" section — clarify that Inline Terminal now uses the shell detected from VS Code's terminal.integrated.defaultProfile setting, rather than always defaulting to cmd.exe (Windows) or sh (Unix).
  2. Inline Terminal vs VS Code Terminal comparison table — update the shell selection row to reflect that both modes now respect the user's configured default shell.
  3. Add WSL entry — document that WSL terminals do not support shell integration; commands fall back to inline (execa) execution via direct wsl.exe spawn.
  4. Add Windows troubleshooting entry — document that on non-ASCII locales (zh-CN, ja-JP, ko-KR, etc.), cmd.exe encodes output using the system codepage rather than UTF-8. Running in the configured shell (e.g., PowerShell) enables easier UTF-8 configuration, though explicit setup is still required.

Additional Notes

None.

Get in Touch

Discord: Yon (yon.sinc)

Zoo Code Contributor and others added 2 commits May 26, 2026 16:26
…ion (Zoo-Code-Org#321)

Connect the existing getShell() to both terminal creation paths to fix
the Windows shell mismatch where AI generates pwsh syntax but commands
execute in cmd.exe.

- ExecaTerminalProcess: use getShell() instead of shell: true
- Terminal: pass shellPath: getShell() to createTerminal

Fixes Zoo-Code-Org#321
…gration (Zoo-Code-Org#321)

Update two test cases that previously expected shell: true to now
expect shell: expect.any(String), reflecting the change from
shell: true to getShell() in ExecaTerminalProcess.

Refs Zoo-Code-Org#321

Co-authored-by: Zoo Code Contributor <contributor@zoocode.dev>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Shell resolution now prefers vscode.env.shell (canonicalizing WSL); ExecaTerminalProcess uses BaseTerminal.getExecaShellPath() || getShell() with a WSL direct wsl.exe path; Terminal uses a constructor-owned shellIntegrationReady promise; TerminalProcess stream handling and tests/mocks updated.

Changes

Terminal shell & integration changes

Layer / File(s) Summary
Shell utilities and tests
src/utils/shell.ts, src/utils/__tests__/shell.spec.ts
Add WSL_EXE_PATH, getWslProfile(), and rewrite getShell() to prefer vscode.env.shell, canonicalize WSL, and fallback via userInfo/env/platform; tests reworked for new priority and WSL detection.
ExecaTerminalProcess execution and tests
src/integrations/terminal/ExecaTerminalProcess.ts, src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
Use `BaseTerminal.getExecaShellPath()
Terminal constructor & run gating
src/integrations/terminal/Terminal.ts
Create VS Code terminal with WSL-aware shell behavior and add a constructor-owned shellIntegrationReady promise (event + polling + timeout); runCommand awaits that promise before process.run.
TerminalProcess stream handling and tests
src/integrations/terminal/TerminalProcess.ts, src/integrations/terminal/__tests__/TerminalProcess.spec.ts, src/integrations/terminal/__tests__/TerminalProcess.test.ts
Emit clearer no_shell_integration warnings, avoid duplicate emission after executeCommand submission, treat missing shell-integration start markers as normal buffered output, and update tests to match changed sendText expectations and mocks.
VSCode mock and registry/exec tests
src/__mocks__/vscode.js, src/integrations/terminal/__tests__/*
Extend Vitest vscode mock with env.shell and window.onDidChangeTerminalShellIntegration; update exec and registry tests to rely on vscode.env.shell, tighten iconPath assertions, and standardize mock reset between tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#239: Adjusts Windows shell resolution in src/utils/shell.ts similarly to ensure PowerShell is used instead of falling back to cmd.exe.

Suggested reviewers

  • taltas
  • JamesRobert20
  • navedmerchant
  • hannesrudolph

Poem

🐰 I nibble paths and sniff each shell,

wsl or bash, they ring a bell,
getShell guides where commands should run,
no more cmd.exe surprise for anyone,
Hooray — a hop, a bug undone!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing shell mismatch by using the detected shell for both execa and VS Code terminal creation.
Linked Issues check ✅ Passed The code changes directly address issue #321 by connecting getShell() to both execa subprocess execution and VS Code terminal creation, eliminating the shell mismatch between AI prompt generation and Inline Terminal execution.
Out of Scope Changes check ✅ Passed All changes are focused on the shell-detection objective. Test updates align with implementation changes. Mock updates support the shell-detection flow. The vscode mock additions (env.shell, onDidChangeTerminalShellIntegration) and shell.ts refactoring (getWslProfile, WSL_EXE_PATH) are all necessary infrastructure for proper shell resolution.
Description check ✅ Passed The PR description is comprehensive and well-structured. It follows the template with all required sections completed: Related GitHub Issue (links to #321), detailed Description explaining the problem and two-phase solution, Test Procedure with specific commands, Pre-Submission Checklist fully completed, and Documentation Updates identified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@F915 F915 marked this pull request as ready for review May 26, 2026 10:13
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f5fb7a1-993e-4d1d-987f-4466eb7afe09

📥 Commits

Reviewing files that changed from the base of the PR and between b761a0a and dc4a451.

📒 Files selected for processing (3)
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts

Comment thread src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts Outdated
Mock getShell() to verify the fallback actually uses the detected shell
instead of just checking for any string value.

Suggested-by: CodeRabbit
@edelauna edelauna closed this May 26, 2026
@edelauna edelauna reopened this May 26, 2026
F915 and others added 3 commits May 27, 2026 09:40
…ll() integration (Zoo-Code-Org#321)

PR Zoo-Code-Org#333 added shellPath and iconPath to Terminal constructor but
TerminalRegistry.spec.ts assertions were not updated, causing 4 test
failures on ubuntu-latest CI.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/integrations/terminal/__tests__/TerminalRegistry.spec.ts (1)

39-40: ⚡ Quick win

Assert getShell() invocation to prevent hardcoded-shell regressions.

You already assert the resulting shellPath, but adding expect(shellUtils.getShell).toHaveBeenCalled() makes this test fail if production code hardcodes the same string instead of calling the resolver.

Suggested test hardening
 		it("creates terminal with PAGER set appropriately for platform", () => {
 			TerminalRegistry.createTerminal("/test/path", "vscode")
+			expect(shellUtils.getShell).toHaveBeenCalledTimes(1)

 			expect(mockCreateTerminal).toHaveBeenCalledWith({
 				cwd: "/test/path",
 				name: "Roo Code",
 				iconPath: expect.objectContaining({ id: expect.any(String) }),
 				env: {
 					PAGER,
 					ROO_ACTIVE: "true",
 					VTE_VERSION: "0",
 					PROMPT_EOL_MARK: "",
 				},
 				shellPath: "/mock/fallback-shell",
 			})
 		})

Also applies to: 60-61, 83-84, 107-108, 130-131

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalRegistry.spec.ts` around lines 39
- 40, Add assertions that the shell resolver is actually invoked: after each
vi.spyOn(shellUtils, "getShell").mockReturnValue("/mock/fallback-shell") in the
TerminalRegistry.spec tests, add expect(shellUtils.getShell).toHaveBeenCalled()
(or toHaveBeenCalledTimes(1)) to ensure production code calls
shellUtils.getShell rather than hardcoding the path; apply this to the
occurrences around the tests that set up mocked shells (the blocks using
vi.spyOn(shellUtils, "getShell") and asserting shellPath).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/integrations/terminal/__tests__/TerminalRegistry.spec.ts`:
- Around line 39-40: Add assertions that the shell resolver is actually invoked:
after each vi.spyOn(shellUtils,
"getShell").mockReturnValue("/mock/fallback-shell") in the TerminalRegistry.spec
tests, add expect(shellUtils.getShell).toHaveBeenCalled() (or
toHaveBeenCalledTimes(1)) to ensure production code calls shellUtils.getShell
rather than hardcoding the path; apply this to the occurrences around the tests
that set up mocked shells (the blocks using vi.spyOn(shellUtils, "getShell") and
asserting shellPath).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 11da761d-d24d-4448-bec2-2eb4363d25fa

📥 Commits

Reviewing files that changed from the base of the PR and between 9205427 and 3ae0a88.

📒 Files selected for processing (1)
  • src/integrations/terminal/__tests__/TerminalRegistry.spec.ts

@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/integrations/terminal/ExecaTerminalProcess.ts 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@proyectoauraorg proyectoauraorg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Using the detected shell for both execa and VS Code terminal creation resolves the shell mismatch issue on Windows. Clean fix.

Comment thread src/integrations/terminal/Terminal.ts Outdated
const env = Terminal.getEnv()
const iconPath = new vscode.ThemeIcon("rocket")
this.terminal = terminal ?? vscode.window.createTerminal({ cwd, name: "Roo Code", iconPath, env })
this.terminal = terminal ?? vscode.window.createTerminal({ cwd, name: "Roo Code", iconPath, env, shellPath: getShell() })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user has WSL configured but no explicit shell path, getShell() returns /bin/bash (a Unix path) — does passing that as shellPath actually work on Windows, or would wsl.exe be needed here?

Copy link
Copy Markdown
Author

@F915 F915 May 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and you're right that this was a real issue.

After further analysis, we've addressed this in the follow-up refactoring (commit 387ca89). The terminal constructor now checks for WSL configuration first — when WSL is detected, shellPath is omitted so VS Code's own WSL profile handles shell selection. Passing an explicit shell path would bypass VS Code's profile system.

With this fix, the inline terminal path can now correctly execute WSL commands. However, the external terminal path still cannot work with WSL, because VS Code shell integration does not support WSL — the supported shells on Windows are limited to Git Bash and pwsh. In practice, the external terminal will wait for shell integration until timeout, then fall back to the inline terminal — which is the expected behavior.

These changes are part of a broader consolidation of shell detection that the WSL issue prompted us to revisit. The upstream code manually parsed VS Code's terminal profile configuration with a shell allowlist, but this approach was inherently fragile — it could not reliably match VS Code's internal shell resolution, which is exactly the kind of mismatch your comment pointed out. To address this at the root, we removed the allowlist and manual parsing in favor of vscode.env.shell, the API VS Code has provided for this purpose. As a result, the two command execution paths now share the same shell resolution logic, so there is no longer a risk of falling back to a different shell than the one the external terminal used. A safety check that guarded against this mismatch — but relied on the same fragile detection it was meant to protect against — became redundant and was removed.

We also removed a sendText call that previously fired before the fallback when shell integration was unavailable — this could cause the command to execute in the external terminal without output capture, and then again via the inline path, resulting in unintended double execution. The fallback to the inline terminal now happens directly.

Thanks again for taking the time to review — the feedback was helpful in tracing this issue to its root cause.

@F915 F915 deleted the branch Zoo-Code-Org:main May 29, 2026 01:27
@F915 F915 closed this May 29, 2026
@F915 F915 deleted the main branch May 29, 2026 01:27
@F915 F915 restored the main branch May 29, 2026 01:42
@F915 F915 reopened this May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts (1)

176-212: 💤 Low value

Unusual semicolon-prefixed statement formatting.

Lines 180, 189, 198, and 207 use a semicolon prefix before calling trimRetrievedOutput(). This is syntactically valid but unconventional and reduces readability.

♻️ Cleaner formatting
 		it("clears buffer when all output has been retrieved", () => {
 			terminalProcess["fullOutput"] = "test output data"
 			terminalProcess["lastRetrievedIndex"] = 16
-				; (terminalProcess as any).trimRetrievedOutput()
+			;(terminalProcess as any).trimRetrievedOutput()

 			expect(terminalProcess["fullOutput"]).toBe("")

Or wrap the call to avoid ASI ambiguity:

-			terminalProcess["lastRetrievedIndex"] = 16
-				; (terminalProcess as any).trimRetrievedOutput()
+			terminalProcess["lastRetrievedIndex"] = 16
+			const instance = terminalProcess as any
+			instance.trimRetrievedOutput()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts` around
lines 176 - 212, The tests use unconventional semicolon-prefixed calls like
;(terminalProcess as any).trimRetrievedOutput() which is unnecessary and harms
readability; update each test to call the method directly (e.g.,
(terminalProcess as any).trimRetrievedOutput()) or otherwise wrap the expression
to avoid the leading semicolon—modify the four occurrences that invoke
trimRetrievedOutput on the terminalProcess instance to remove the prefixed
semicolons.
src/utils/__tests__/shell.spec.ts (1)

203-263: 💤 Low value

Consider restoring the original getConfiguration mock after each getWslProfile test.

The tests directly reassign vscode.workspace.getConfiguration without restoring it, which could cause test pollution if tests run in a different order or if new tests are added to this describe block.

♻️ Suggested improvement
 	describe("getWslProfile()", () => {
+		let originalGetConfiguration: typeof vscode.workspace.getConfiguration
+
 		beforeEach(() => {
 			Object.defineProperty(process, "platform", { value: "win32" })
+			originalGetConfiguration = vscode.workspace.getConfiguration
+		})
+
+		afterEach(() => {
+			vscode.workspace.getConfiguration = originalGetConfiguration
 		})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/__tests__/shell.spec.ts` around lines 203 - 263, The tests for
getWslProfile directly overwrite vscode.workspace.getConfiguration and never
restore it, risking test pollution; update the describe("getWslProfile") block
to save the original getConfiguration (e.g., const originalGetConfig =
vscode.workspace.getConfiguration) in a beforeEach and restore it in an
afterEach (or use jest.spyOn(vscode.workspace, "getConfiguration") and call
mockRestore() in afterEach) so each it(...) can safely mock getConfiguration
without leaking state across tests.
src/integrations/terminal/ExecaTerminalProcess.ts (1)

37-88: 💤 Low value

effectiveCommand is assigned but unused in the WSL branch.

The variable effectiveCommand on line 47 is only used in the non-WSL else branch (line 87), but it's assigned regardless. This is minor dead code when isWslShell is true.

♻️ Suggested cleanup
 		const resolvedShell = BaseTerminal.getExecaShellPath() || getShell()
 		const isWslShell = resolvedShell === WSL_EXE_PATH

-		let effectiveShell: string | boolean = resolvedShell
-		let effectiveCommand = command
-
 		if (isWslShell) {
 			// Spawn wsl.exe directly (not through cmd.exe) to avoid nested-quoting issues.
 			// execa(file, args, options) passes args as an array — no shell interpretation.
@@ ... @@
 		} else {
 			this.subprocess = execa({
-				shell: effectiveShell,
+				shell: resolvedShell,
 				cwd: this.terminal.getCurrentWorkingDirectory(),
 				all: true,
 				stdin: "ignore",
 				env: {
 					...process.env,
 					LANG: "en_US.UTF-8",
 					LC_ALL: "en_US.UTF-8",
 				},
-			})`${effectiveCommand}`
+			})`${command}`
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/ExecaTerminalProcess.ts` around lines 37 - 88, The
run method assigns effectiveCommand but never uses it in the WSL branch; remove
the dead assignment by either moving the line that sets effectiveCommand into
the non-WSL else branch or stop using effectiveCommand entirely and pass command
directly to the non-WSL execa template literal—update the run function
(referencing run, effectiveCommand, isWslShell, and the execa calls) so
effectiveCommand is only created/used where needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts`:
- Around line 52-54: The env object literal sets shell to
"C:\Windows\System32\cmd.exe" with unescaped backslashes; update the shell
string in the test (the env object, shell property) to use escaped backslashes
("C:\\Windows\\System32\\cmd.exe") so the path is a valid JavaScript string
literal and avoids accidental escape sequences.

In `@src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts`:
- Around line 52-54: The Windows path string assigned to env.shell currently
uses single backslashes which are interpreted as escape sequences; update the
test to use escaped backslashes (e.g., double backslashes) in the "shell" value
so the path reads correctly (replace "C:\Program Files\PowerShell�\pwsh.exe"
with an escaped form); locate the env object in the
TerminalProcessExec.pwsh.spec.ts test and change the shell string to use "\\"
for each backslash.

---

Nitpick comments:
In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts`:
- Around line 176-212: The tests use unconventional semicolon-prefixed calls
like ;(terminalProcess as any).trimRetrievedOutput() which is unnecessary and
harms readability; update each test to call the method directly (e.g.,
(terminalProcess as any).trimRetrievedOutput()) or otherwise wrap the expression
to avoid the leading semicolon—modify the four occurrences that invoke
trimRetrievedOutput on the terminalProcess instance to remove the prefixed
semicolons.

In `@src/integrations/terminal/ExecaTerminalProcess.ts`:
- Around line 37-88: The run method assigns effectiveCommand but never uses it
in the WSL branch; remove the dead assignment by either moving the line that
sets effectiveCommand into the non-WSL else branch or stop using
effectiveCommand entirely and pass command directly to the non-WSL execa
template literal—update the run function (referencing run, effectiveCommand,
isWslShell, and the execa calls) so effectiveCommand is only created/used where
needed.

In `@src/utils/__tests__/shell.spec.ts`:
- Around line 203-263: The tests for getWslProfile directly overwrite
vscode.workspace.getConfiguration and never restore it, risking test pollution;
update the describe("getWslProfile") block to save the original getConfiguration
(e.g., const originalGetConfig = vscode.workspace.getConfiguration) in a
beforeEach and restore it in an afterEach (or use jest.spyOn(vscode.workspace,
"getConfiguration") and call mockRestore() in afterEach) so each it(...) can
safely mock getConfiguration without leaking state across tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dff403bf-4ce8-4795-9eca-970a7a523af7

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae0a88 and b255557.

📒 Files selected for processing (13)
  • src/__mocks__/vscode.js
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/TerminalProcess.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcess.test.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.bash.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts
  • src/integrations/terminal/__tests__/TerminalRegistry.spec.ts
  • src/utils/__tests__/shell.spec.ts
  • src/utils/shell.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/integrations/terminal/tests/TerminalRegistry.spec.ts

Comment thread src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts
…-Org#321)

Replace manual VS Code config parsing and allowlist validation in shell.ts
with vscode.env.shell, the API VS Code provides for extension shell
detection since version 1.37.

- shell.ts: remove SHELL_ALLOWLIST (~95 entries) and manual config parsing
  functions (getWindowsShellFromVSCode, getMacShellFromVSCode,
  getLinuxShellFromVSCode). getShell() reads vscode.env.shell directly,
  falling back through os.userInfo(), COMSPEC/SHELL environment variables,
  and platform defaults. Remove getShellFallbackOccurred().
- Terminal.ts: construct terminal without explicit shellPath when the user
  has not set execaShellPath or configured WSL, letting VS Code's profile
  system determine the shell. When execaShellPath is set, pass it explicitly.
  For WSL, omit shellPath so VS Code uses its WSL profile for shell
  integration. Start the shell-integration-ready promise in the constructor.
- TerminalProcess.ts: remove the getShellFallbackOccurred check that
  previously guarded against mismatch between the terminal's shell and the
  detected shell. After the shell.ts refactoring, both paths use the same
  VS Code resolution. When shell integration is unavailable, emit
  no_shell_integration without calling sendText first.
  In the stream processing path, treat missing OSC 633/133 markers as
  complete output instead of an error.
- ExecaTerminalProcess.ts: use BaseTerminal.getExecaShellPath() || getShell()
  to match Terminal's shell resolution. When the shell is wsl.exe, spawn it
  directly with array arguments instead of wrapping through a shell.
- tests: update shell.spec.ts for the new getShell() fallback chain. Update
  terminal test files to mock vscode.env.shell instead of stubbing getShell().
  Extend vscode.js mock with onDidChangeTerminalShellIntegration event and
  env.shell. Update TerminalProcess.spec.ts to verify sendText is not called
  before no_shell_integration (preventing double execution). Update
  TerminalRegistry.spec.ts to drop the getShell spy and shellPath assertions
  now that Terminal no longer passes an explicit shellPath by default. Add
  WSL direct-spawn test in ExecaTerminalProcess.spec.ts.

Fixes Zoo-Code-Org#321
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Windows: shell mismatch between AI prompt generation and Inline Terminal execution

3 participants